﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NLog;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Text;

namespace Stee.CAP8.Communication.HttpInterface
{
    /// <summary>
    /// Extensions methods to configure an <see cref="IServiceCollection"/> for <see cref="IHttpClientFactory"/> and  T/>.
    /// </summary>
    public static class ServiceCollectionExtension
    {
        /// <summary>
        /// Add the user defined interface and  <see cref="IHttpClientFactory"/> to the <see cref="IServiceCollection"/>.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/>.</param>
        /// <param name="configurationSection">The developer's <see cref="IConfigurationSection"/> to use.</param>
        /// <returns>The <see cref="IServiceCollection"/>.</returns>
        public static IServiceCollection AddHttpClientProxyService<T>(this IServiceCollection serviceCollection, IConfigurationSection configurationSection)
        {
            if (serviceCollection == null)
            {
                throw new ArgumentNullException(nameof(serviceCollection));
            }

            if (configurationSection == null)
            {
                throw new ArgumentNullException($"Configuration Section can not be null! Object Name: {nameof(configurationSection)}");
            }

            var httpClientOption = new HttpClientOption();
            configurationSection.Bind(httpClientOption);

            if (string.IsNullOrEmpty(httpClientOption.BaseAddress))
            {
                throw new ArgumentNullException($"BaseAddress can not be null! Object Name: {nameof(configurationSection)}");
            }

            serviceCollection.AddHttpClient(typeof(T).Name).ConfigureHttpClientOptions(httpClientOption);
            var serviceProvider = serviceCollection.BuildServiceProvider();
            
            var contractAttr = typeof(T).GetCustomAttribute<HttpContractAttribute>()
                  ?? throw new ArgumentException($"Contract type must be annotated with {typeof(HttpContractAttribute).Name}.");
            var httpClientProxy = DispatchProxy.Create<T, HttpClientProxy>();
            (httpClientProxy as HttpClientProxy).InitialInstance(serviceProvider.GetService<IHttpClientFactory>(), contractAttr.Namespace);

            LogHelper.WriteLog(LogLevel.Info, $"ServiceCollection extension create instance: {typeof(T).Name}");
            return serviceCollection.AddScoped(typeof(T), provider => httpClientProxy);
        }

        /// <summary>
        /// Extensions methods for <see cref="IHttpClientBuilder"/> to configure <see cref="IHttpClientFactory"/>.  
        /// </summary>
        /// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
        /// <param name="httpClientOption">A <see cref="HttpClientOption"/> that is used to set an <see cref="IHttpClientFactory"/>.</param>
        /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
        internal static IHttpClientBuilder ConfigureHttpClientOptions(this IHttpClientBuilder builder, HttpClientOption httpClientOption)
        {
            if (httpClientOption == null)
            {
                return builder;
            }

            builder.ConfigureHttpClient(httpClient =>
            {
                if (!string.IsNullOrEmpty(httpClientOption.BaseAddress))
                {
                    httpClient.BaseAddress = new Uri(httpClientOption.BaseAddress);
                }

                httpClient.Timeout = TimeSpan.FromSeconds(httpClientOption.TimeOut);

            }).ConfigurePrimaryHttpMessageHandler(() =>
                 {
                     return new HttpClientHandler()
                     {
                         ClientCertificateOptions = ClientCertificateOption.Automatic,
                         ServerCertificateCustomValidationCallback = (message, cert, chain, error) =>
                         {
                             switch (httpClientOption.HttpsCertCheck)
                             {
                                 case CertCheckType.CheckCertIssuer:
                                     if (!string.IsNullOrEmpty(httpClientOption.CertIssuer))
                                     {
                                         return cert.Issuer.Equals(httpClientOption.CertIssuer, StringComparison.OrdinalIgnoreCase);
                                     }
                                     return false;
                                 default:
                                     return true;
                             }
                         }
                     };
                 });

            return builder;
        }
    }
}
